/*
 * Console.c
 *
 *  Created on: Oct 14, 2018
 *      Author: ax
 */
#include <Console.h>
#include <Process.h>

#include <Sos.h>


#define INPUT_LINES 2//ping - pong buff should be enough
typedef enum {
    LINE_USEABLE = 0,
    LINE_INPUT,
    LINE_READY,
    LINE_INUSE,
    LINE_STATUS_MAX,
    LINE_INVALID_STATUS
} CmdLineStatus;

typedef struct {
    Uint8  len;
    Uint8  sts;
    Bits16 flg;
    Printf eco;
    char   cmd[Console_CMD_LINE_MAX_LEN];
} Console_CommandLine;

Console_CommandLine __buffer(Console) CommandLines[INPUT_LINES] = {LINE_USEABLE};


///FIXME move these to Sos
static struct {
    short cmd;
    short argcMax;
    Process entry;
} cond[] = {
    {CMD('q','m'),  5, (Process) qm}, //qm {c|e|d} [queue] [desc] [size]
    {CMD('m','w'),  2, (Process) mw}, //mw {width}
    {CMD('m','d'),  2, (Process) md}, //md {address} [size]
    {CMD('m','m'),  3, (Process) mm}, //mm {addr value}
    //TODO insert above!
    {CMD(0,    0), Console_CMD_ARGS_CNT_MAX, (Process) iv}
};

const short Console_CmdMaxSupport = sizeof(cond) / 8 - 1;//exclude last iv


static int printf_null(const char* fmt, ...) {return 0;}
Printf Console_printf = printf_null;


char*
Tty_commandLineAlloc(
    Printf outf
){
    short idx;

    if (CommandLines[0].sts == LINE_USEABLE)
        idx = 0;
    else
        idx = 1;

    CommandLines[idx].eco = outf;
    CommandLines[idx].sts = LINE_INPUT;

   return &CommandLines[idx].cmd[0];
}


void
Tty_commandLineFree(
    void* line
){
    if ((unsigned)line == (unsigned)&CommandLines[0].cmd)
        CommandLines[0].sts = LINE_READY;
    else
        CommandLines[1].sts = LINE_READY;

    Process_resume(Process_getIdByName(cosD));
}


static inline
Console_CommandLine*
Console_commandLineNext(
    void
){
    short idx = -1;

    if (CommandLines[0].sts == LINE_READY)
        idx = 0;
    else if (CommandLines[1].sts == LINE_READY)
        idx = 1;

    if (idx != -1) {
        CommandLines[idx].sts = LINE_READY;
        return &CommandLines[idx];
    } else
        return NULL;
}


static inline
void
Console_commandLinePost(
    void* line
){
    if (!line)
        return;

    if ((unsigned)line == (unsigned)&CommandLines[0].cmd)
        CommandLines[0].sts = LINE_USEABLE;
    else
        CommandLines[1].sts = LINE_USEABLE;
}


static short
cmdParse(
    char*   line,
    Uint16* argc,
    void*   argv[]
){
    const char blank = ' ';
    short cmd = CMD(line[0], line[1]);

    short   cnt = 1;
    argv[0] = line;

    char ch;
    while ((ch = *++line) != 0) {
        if (ch == blank) {
            argv[++cnt] = ++line;
    }   }

    *argc = cnt;

    return cmd;
}


void
__daemon(console)
Consoled(
    void
){
    Console_CommandLine* cmdLine;
    while ((cmdLine = Console_commandLineNext()) != NULL) {
        Uint16 argc;
        void* argv[Console_CMD_ARGS_CNT_MAX];
        Cmd cmd = cmdParse(&cmdLine->cmd[0], &argc, argv);

        short i;
        Process entry;
        //find the command entry
        for (i = 0; i < Console_CmdMaxSupport; i++)
            if (cmd == cond[i].cmd)
                entry = cond[i].entry;

        //unsupported command (not found), use iv method echo back
        if (i == Console_CmdMaxSupport)
            entry = cond[i].entry;

        //redirect print routine
        Console_printf = cmdLine->eco;
        entry(argc, argv);

        Console_printf = printf_null;
        Console_commandLinePost(cmdLine);

    }
}


int
iv(
    int argc,
    void* argv[]
){
    Console_printf("[Console]: invalid command %s!", argv[0]);

    return 0;
}
